/*    
OpenMheg - A free MHEG-5 library
Copyright (C) 2008 Dharmesh Malam

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Reflection;

namespace GUI.Renderer.GDI.Drawers
{
    public class TextDrawer : Drawer
    {
        public override Type GetVisible
        {
            get
            {
                Assembly assembly = Assembly.Load( "Core" );
                return assembly.GetType( "Core.Model.Text", true );
            }
        }

        public override void Draw ( Core.Model.Visible v, Graphics g )
        {
            g.PageUnit = GraphicsUnit.Pixel;

            Core.Model.Text text = ( Core.Model.Text ) v;

            if ( text.BoxSize.Height == 0 || text.BoxSize.Width == 0 ) return;
                 

            FontFamily fontFamily = new FontFamily(MHEGDisplayConstants.DefaultFontName);

            FontStyle fontStyle = text.FontAttributes.FontStyle;

            Font font;

            try
            {
               font = new Font(fontFamily, text.FontAttributes.FontSize - 7, fontStyle, GraphicsUnit.Pixel);
            }
            catch (Exception ex)
            {
               font = new Font(new FontFamily("Arial"), text.FontAttributes.FontSize - 7, fontStyle, GraphicsUnit.Pixel);
            }

            StringFormatFlags stringFormatFlags;// = StringFormatFlags.NoClip;
            
            stringFormatFlags = StringFormatFlags.LineLimit;

            if ( !text.TextWrapping )
                stringFormatFlags |= StringFormatFlags.NoWrap;

            if ( text.LineOrientation == Core.Model.Text.TextOrientation.Vertical )
                stringFormatFlags |= StringFormatFlags.DirectionVertical;

            
            StringFormat stringFormat = new StringFormat( stringFormatFlags );

            switch ( text.HorizontalJustification )
            {
                case Core.Model.Text.TextJustification.Centre:
                    stringFormat.Alignment = StringAlignment.Center;
                    break;
                case Core.Model.Text.TextJustification.End:
                    stringFormat.Alignment = StringAlignment.Far;
                    break;
                case Core.Model.Text.TextJustification.Start:
                    stringFormat.Alignment = StringAlignment.Near;
                    break;
                case Core.Model.Text.TextJustification.Justified:
                    stringFormat.Alignment = StringAlignment.Near;
                    break;
            }

            switch ( text.VerticalJustification )
            {
                case Core.Model.Text.TextJustification.Centre:
                    stringFormat.LineAlignment = StringAlignment.Center;
                    break;
                case Core.Model.Text.TextJustification.End:
                    stringFormat.LineAlignment = StringAlignment.Far;
                    break;
                case Core.Model.Text.TextJustification.Start:
                    stringFormat.LineAlignment = StringAlignment.Near;
                    break;
                case Core.Model.Text.TextJustification.Justified:
                    stringFormat.LineAlignment = StringAlignment.Near;
                    break;

            }

            

            // Tabs are set every 56 point
            stringFormat.SetTabStops(0,new float[]{10});
            stringFormat.Trimming = StringTrimming.Character;

            //String textData = DoMarkup( text.TextData);

            if ( stringFormat.LineAlignment == StringAlignment.Near)
            {

                List<SubLine> sublines = DoMarkup( text.TextData, text.BoxArea, text.TextColour.Value, text.FontAttributes.LineSpacing, text.TextWrapping, g, font, stringFormat );

                //if ( stringFormat.LineAlignment == StringAlignment.Center )
                //{ }
                //else if ( stringFormat.LineAlignment == StringAlignment.Far )
                //{ }

                //if ( sublines.Count >1)
                //    stringFormat.LineAlignment = StringAlignment.Near;

                //System.Drawing.Rectangle paper = new System.Drawing.Rectangle( text.BoxPosition, text.BoxSize );

                using ( Brush paperBrush = new SolidBrush( text.BackgroundColour.Value ) )
                {
                    g.FillRectangle( paperBrush, text.BoxArea );


                    foreach ( SubLine l in sublines )
                    {
                        using ( Brush brush = new SolidBrush( l.Colour ) )
                        {
                            g.DrawString( l.Text, font, brush, l.Start, stringFormat );
                            
                        }

                    }



                    // }
                }


            }

            else
            {
                using ( Brush brush = new SolidBrush( text.TextColour.Value ) )
                {
                    if ( text.TextData.Length < 1000 )
                        g.DrawString(RMMark(text.TextData), font, brush, text.BoxArea, stringFormat );
                }
            }


        }

        String RMMark ( String s )
        {
            char [] cs = s.ToCharArray();
            List<Char> charsBuff = new List<char>();
            
            for(int i =0; i< cs.Length;i++)
            {
                if ( cs [i] == '\r' ) 
                {
                    charsBuff.Add('\r');
                    charsBuff.Add('\n');
                }
                else if ( cs[i] == 0x1b )
                {
                    i++;
                    //c = cs [i];
                    if ( cs[i] >= 0x40 && cs[i] <= 0x5e )
                    {

                        i+=5;


                    }
                    else if ( cs[i] >= 0x60 && cs[i] <= 0x7e )
                    {
                        i++;

                    }
                    
                    
                }
                else if ( cs[i] <= 0x1f && cs[i] >=0x1c)
                {
                }
                else
                {
                    
                    if ( ( int ) cs [i] >= 192 ) //read 2 bytes
                    {
                        Byte [] bytes = new Byte [4];

                        int j = 1;
                        //int c = ( int ) cs [i] << 8 | (int) cs[i+1];
                        bytes[0] = (byte) cs[i];
                        bytes[1] = (byte) cs[i+1];

                        if ( ( int ) cs [i] >= 224 ) //read 3 bytes
                        {
                            //c = c << 8 | ( int ) cs [i+2];
                            bytes[2] = (byte) cs[i+2];
                            j++;

                            if ( ( int ) cs [i] >= 240 ) //read 4 bytes
                            {
                                j++;
                                //c = c << 8 | ( int ) cs [i+3];
                                bytes[3] = (byte) cs[i+3];
                            }

                        }


                        Char[] chars = new char[1];
                        Encoding.UTF8.GetDecoder().GetChars( bytes,0, j+1, chars, 0, true );


                       charsBuff.Add( chars[0]);

                       i +=j;
                    }
                    else
                    charsBuff.Add( cs [i] );
                }

            }

            return new String( charsBuff.ToArray() );
        }



        List<SubLine> DoMarkup ( String text, Rectangle layout, Color top, int lineSpacing, bool wrapping, Graphics g, Font font, StringFormat stringFormat )
        {
            if (text == null) throw new NullReferenceException();
            Stack<Color> colours = new Stack<Color>();
            colours.Push(top);
            int tabstop = 45;

            char [] cs = text.ToCharArray();
            List<Char> charsBuff = new List<Char>();
            List<SubLine> sublines = new List<SubLine>();
            Point currentPos = new Point(layout.X,layout.Y);
            //Char c;

            int i = 0;
            while( i <cs.Length )
            {
              //  c = cs [i];

                if ( Char.IsWhiteSpace( cs[i] ) )
                {
                    if ( cs [i] == '\r' )
                    {
                        
                        sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos));
                        currentPos.X = layout.X;
                        currentPos.Y += lineSpacing;
                        charsBuff = new List<Char>();
                        //charsPost.Add( '\n' );
                        //charsPost.Add( cs [i] );
                    }
                    else if(cs[i] == '\t'){

                        if ( stringFormat.Alignment == StringAlignment.Near )
                        {
                            sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos ) );

                            int curEnd = currentPos.X + (int)Math.Round( g.MeasureString( new String( charsBuff.ToArray() ), font ).Width );
                            int newPos=   curEnd + tabstop - ( (curEnd-layout.X) % tabstop );

                            if ( newPos > layout.X + layout.Width )
                            {
                                currentPos.Y += lineSpacing;
                                currentPos.X = layout.X;
                            }
                            else
                            {
                                if ( newPos -curEnd <= 1 )
                                    newPos += tabstop;

                                currentPos.X = newPos;
                            }


                            charsBuff = new List<Char>();
                        }
                        else charsBuff.Add( ' ' );
                    }
                    else
                        charsBuff.Add( cs [i] );


                }
                else if ( cs[i] == 0x1b )
                {
                    i++;
                    //c = cs [i];
                    if ( cs[i] >= 0x40 && cs[i] <= 0x5e )
                    {
                        Color colour;
                        int numParas = ( int ) cs[i+1];

                        if ( cs [i] == 0x42 ) { i++; }
                        else if ( cs [i] == 0x43 && numParas == 4 && cs.Length > i+5 )
                        {
                            int aa =255, rr=0, gg=0, bb=0;
                            i++;
                            //i++;
                            rr = ( int ) cs [++i] & 0xFF;
                            gg = ( int ) cs [++i] & 0xFF;
                            bb = ( int ) cs [++i] & 0xFF;
                            aa -= ( int ) cs [++i] & 0xFF;

                            //if ( rr == 0 && gg!=0 && bb != 00 && aa != 255)
                            //{
                            //    colour = Color.White;
                            //    i -= 3;
                            //}
                            //else if ( rr == 127 && gg == 0 )
                            //{
                            //    //colour = Color.PeachPuff;
                            //    colour = top;
                            //    i-=2;
                            //}
                            //else if ( rr==63 && gg == 0 )
                            //{
                            //    colour = Color.Aqua;
                            //    i-=2;
                            //}
                            //else if ( rr==127 && gg ==127 && bb == 0 )
                            //{
                            //    colour = Color.FromArgb( rr, gg, bb );
                            //    i-=1;
                            //}
                            //else if ( rr == 0 && gg == 0 && bb!=0 && aa !=0 )
                            //{
                            //    colour = Color.Yellow;
                            //    i-=2;
                            //}
                            //else
                            //{
                                colour = Color.FromArgb( aa, rr, gg, bb );
                           // }


                            //if ( rr!=0 && cs.Length > i )
                            //{
                            //    i++;
                            //    gg = ( int ) cs [i];

                            //    if ( cs.Length > i )
                            //    {
                            //        i++;
                            //        bb = ( int ) cs [i];
                            //        if ( cs.Length > i )
                            //        {
                            //            i++;
                            //            aa -= ( int ) cs [i+4];
                            //            colour = Color.FromArgb( aa, rr, gg, bb );
                            //        }
                            //        //else
                            //         //   colour = Color.FromArgb( aa, rr, gg, bb );
                            //    }
                            //    //else
                            //      //  colour = Color.PeachPuff;
                            //}
                            //else
                            //    colour = Color.White;

                            if ( charsBuff.Count != 0 )
                            {
                                sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos ) );
                                currentPos.X += ( int ) Math.Round( ( g.MeasureString( new String( charsBuff.ToArray() ), font ,(layout.X +layout.Width) - currentPos.X ,stringFormat) ).Width );
                                charsBuff = new List<Char>();
                            }
                            colours.Push( colour );

                            //i += numParas;
                        }
                        else
                        { i++; i++; }

                        //else
                            //throw new Exception( "Unknown markup Character code" );


                    }
                    else if ( cs[i] >= 0x60 && cs[i] <= 0x7e )
                    {
                        if ( cs [i] == 0x62)
                        {
                        }
                        else if ( cs [i] == 0x63 )
                        {
                            sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos ) );
                            currentPos.X += ( int ) Math.Round( ( g.MeasureString( new String( charsBuff.ToArray() ), font, ( layout.X +layout.Width ) - currentPos.X, stringFormat ) ).Width );
                            charsBuff = new List<Char>();
                            
                            if(colours.Count >1)
                                colours.Pop();
                           // charsPost.Add( '>' );
                        }
                        else
                            throw new Exception( "Unknown markup Character code" );
                        

                    }
                    else
                        throw new Exception( "Dodgy characters" );

                    
                }
                else if ( cs[i] <= 0x1f && cs[i] >=0x1c)
                {
                }
                else
                {
                    if ( wrapping && cs [i] != ' '  && cs [i] != '\t' &&
                        ( ( currentPos.X +
                        ( g.MeasureString( new String( charsBuff.ToArray() ), font, ( layout.X +layout.Width ) - currentPos.X, stringFormat ) ).Width 
                        + ( g.MeasureString( new String( cs [i], 1 ), font, ( layout.X +layout.Width ) - currentPos.X, stringFormat ) ).Width ) 
                                    > ( layout.X + layout.Width ) ) )
                    {
                        List<Char> goBack = new List<char>();

                        if ( charsBuff.Count > 0 )
                        {
                            while ( charsBuff.Count > 0 && ( int ) charsBuff [charsBuff.Count-1] != 0x09 && ( int ) charsBuff [charsBuff.Count-1] != 0x20 )
                            {
                                goBack.Add( charsBuff [charsBuff.Count-1] );
                                charsBuff.RemoveAt( charsBuff.Count-1 );
                            }

                            //if ( charsBuff.Count != 0 )
                            //    charsBuff.RemoveAt( charsBuff.Count-1 );

                            if ( !( charsBuff.Count == 0 && currentPos.X == layout.X ) )
                            {

                                sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos ) );
                                currentPos.X = layout.X;
                                currentPos.Y += lineSpacing;
                            }
                            goBack.Reverse();
                            charsBuff = goBack;
                        }

                    }

                    //if ( ( int ) cs [i] >= 192 ) //read 2 bytes
                    //{
                    //    Byte [] bytes = new Byte [4];

                    //    int j = 1;
                    //    //int c = ( int ) cs [i] << 8 | (int) cs[i+1];
                    //    bytes[0] = (byte) cs[i];
                    //    bytes[1] = (byte) cs[i+1];

                    //    if ( ( int ) cs [i] >= 224 ) //read 3 bytes
                    //    {
                    //        //c = c << 8 | ( int ) cs [i+2];
                    //        bytes[2] = (byte) cs[i+2];
                    //        j++;

                    //        if ( ( int ) cs [i] >= 240 ) //read 4 bytes
                    //        {
                    //            j++;
                    //            //c = c << 8 | ( int ) cs [i+3];
                    //            bytes[3] = (byte) cs[i+3];
                    //        }

                    //    }


                    //    Char[] chars = new char[1];
                    //    Encoding.UTF8.GetDecoder().GetChars( bytes,0, j+1, chars, 0, true );


                    //   charsBuff.Add( chars[0]);

                    //   i +=j;
                    //}
                    //else
                    charsBuff.Add( cs [i] );
                }

                i++;
            }

            if (charsBuff.Count > 0)
                sublines.Add( new SubLine( new String( charsBuff.ToArray() ), colours.Peek(), currentPos ) );                      
            //return new String(charsBuff.ToArray());
            return sublines;
        }
    }

    struct SubLine
    {
        private String text;

        public String Text
        {
            get { return text; }
            set { text = value; }
        }
        private Color colour;

        public Color Colour
        {
            get { return colour; }
            set { colour = value; }
        }
        private Point start;

        public Point Start
        {
            get { return start; }
            set { start = value; }
        }

        public SubLine ( String text, Color colour, Point start )
        {
            this.text = text;
            this.colour = colour;
            this.start = start;
        }
    }
}
